using System;
using System.Data;
using System.Xml;

using OR.Reuse.Persistence;
using System.Diagnostics;

namespace OR.Lazarus.Library.DataTier.Source
{
	public class ObjectDetail : OR.Lazarus.Library.DataTier.Source.Lazarus.ObjectDetail
	{
		protected ProjectDetail mProject;
        protected string mPluralName;
        protected string mType;

		public ProjectDetail Project
		{
			get
			{
				return ( this.GetProject() );
			}
			set
			{
				mProject = value;
			}
		}

        public string PluralName
        {
            get
            {
                return (mPluralName);
            }
            set
            {
                mPluralName = value;
            }
        }

        public string Type
        {
            get { return mType; }
            set { mType = value; }
        }

		public ObjectDetail Parent
		{
			get
			{
				return ( this.GetParent() );
			}
		}

		public new ObjectCollection ParentCollection
		{
			get
			{
				return ( this.GetParentCollection() );
			}
			set
			{
				mParentCollection = value;
			}
		}

		public bool IsGeneralizationRoot
		{
			get
			{
				return ( this.GetIsGeneralizationRoot() );
			}
		}

		public ObjectCollection Ancestors
		{
			get
			{
				return ( this.GetAncestry() );
			}
		}

		public ObjectCollection Children
		{
			get
			{
				return ( this.GetChildren() );
			}
		}

		public ObjectCollection Descendants
		{
			get
			{
				return ( this.GetDescendants() );
			}
		}

		protected virtual ObjectCollection GetParentCollection()
		{
			ObjectCollection	vReturn	= null;

			if ( base.ParentCollection != null )
			{
				if ( base.ParentCollection is ObjectCollection )
				{
					vReturn = ( ObjectCollection )base.ParentCollection;
				}
				else
				{
					throw new ApplicationException( "Invalid Collection Type for Object [" + this.Name + "].  ObjectDetail must have a ParentCollection of type ObjectCollection." );
				}
			}

			return ( vReturn );
		}

		protected virtual ProjectDetail GetProject()
		{
			return ( mProject );
		}

		protected virtual ObjectDetail GetParent()
		{
			ObjectDetail vReturn = null;

			if ( this.Implements.Count == 1 )
			{
				vReturn = this.Project.Objects[ this.Implements[ 0 ].Ref ];
			}

			return ( vReturn );
		}

		protected virtual bool GetIsGeneralizationRoot()
		{
			bool vReturn	= false;

			if ( ( this.Ancestors.Count == 1 ) && ( this.Children.Count > 0 ) )
			{
				vReturn = true;
			}

			return ( vReturn );
		}

		public virtual ObjectCollection GetChildren()
		{
			ObjectCollection	vReturn = new ObjectCollection();

			this.GetChildren( vReturn );

			return ( vReturn );
		}

		protected virtual void GetChildren( ObjectCollection pResult )
		{
			foreach ( ObjectDetail vObject in mProject.Objects )
			{
				foreach ( ImplementDetail vImplement in vObject.Implements )
				{
					if ( vImplement.Ref == this.Name )
					{
						pResult.Add( vObject );
					}
				}
			}
		}

		public virtual ObjectCollection GetDescendants()
		{
			ObjectCollection	vReturn = new ObjectCollection();

			this.GetDescendants( vReturn );

			return ( vReturn );
		}

		protected virtual void GetDescendants( ObjectCollection pResult )
		{
			ObjectCollection	vChildren		= this.GetChildren();
			ObjectDetail		vSourceObject	= null;

			foreach ( ObjectDetail vObject in vChildren )
			{
				pResult.Add( vObject );

				vSourceObject = mProject.Objects[ vObject.Name ];

				vSourceObject.GetDescendants( pResult );
			}
		}

		public virtual ObjectCollection GetAncestry()
		{
			ObjectCollection	vReturn	= new ObjectCollection();

			this.GetAncestry( vReturn );

			return ( vReturn );
		}

		protected virtual void GetAncestry( ObjectCollection pResult )
		{
			ObjectDetail	vObject	= null;

			pResult.AddAt( 0, this );

			foreach ( ImplementDetail vImplement in this.Implements )
			{
				vObject = mProject.Objects[ vImplement.Ref ];

				if ( vObject == null )
				{
					throw new ApplicationException( String.Format( "Object [{0}] has an Implements reference to Object [{1}], which does not exist in the model.", this.Name,  vImplement.Ref ) );
				}

				vObject.GetAncestry( pResult );
			}
		}

		public void Initialize()
		{
			this.OnInitialize();
		}

		protected virtual void OnInitialize()
		{
			this.OnInitializeName();

			if ( !DataUtility.IsEmpty( this.TitleField ) && ( this.Fields[ this.TitleField ] == null ) )
			{
				throw new ApplicationException( "ObjectDetail.OnInitialize() failed: The Title Field [" + this.TitleField + "] cannot be found in object [" + this.Name + "]." );
			}
		}

		protected virtual void OnInitializeName()
		{
			if ( String.IsNullOrEmpty( this.Alias ) )
			{
				this.Alias		= this.Name.ToLower();
			}

			if ( String.IsNullOrEmpty( this.LogicalName ) )
			{
				this.LogicalName = this.Name;
			}

			if ( String.IsNullOrEmpty( this.FriendlyName ) )
			{
				this.FriendlyName = this.LogicalName;
			}
		}

		public FieldDetail GetPKField()
		{
			FieldDetail v_oReturn	= null;
			FieldDetail	v_oDefault	= null;

			if ( this.Implements.Count == 0 )
			{
				v_oDefault	= this.Project.DefaultSetting.Fields.GetPKField();
			}

			FieldDetail	v_oInternal	= this.Fields.GetPKField();

			if ( v_oDefault == null )
			{
				v_oReturn = v_oInternal;
			}
			else
			{
				v_oReturn = v_oDefault;
			}

			return ( v_oReturn );
		}

        public override void LoadXml(XmlElement pSource)
        {
            base.LoadXml(pSource);

            this.mPluralName = DataUtility.GetString(pSource, "plural-name", String.Concat(this.Name, "s"));
            this.mType = DataUtility.GetString(pSource, "type", "Default");
        }

        public override XmlElement SaveXml(XmlDocument p_docContext)
        {
            XmlElement rtnVal = base.SaveXml(p_docContext);
            Debug.Assert(rtnVal != null);

            rtnVal.SetAttribute("plural-name", this.PluralName);
            rtnVal.SetAttribute("type", this.Type);

            return rtnVal;
        }
	}
}
